#pragma once

#include <unistd.h>
#include <sys/select.h>
#include <string>
#include "Socket.hpp"
#include "Log.hpp"

static const uint16_t defaultport = 28080;
static const int fd_num_max = sizeof(fd_set) * 8;
static const int defaultfd = -1;

class SelectServer
{
public:
    SelectServer(const uint16_t &port=defaultport)
    :_port(port)
    {
        // 初始化fd数组 全部设置为初始值
        for (int i = 0; i < fd_num_max; i++){
            fd_array[i] = defaultfd;
        }
    }

    bool Init(){
        // 初始化: 创建套接字+绑定+监听
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        return true;
    }

private:
    // 连接事件处理器
    void Accepter(){
        std::string clientip;
        uint16_t clientport = 0;
        int newfd = _listensock.Accept(&clientip, &clientport);
        if(newfd < 0){
            log(Error, "link accept error...");
            return;
        }
        log(INFO, "link accept success..., clientip: %s, clientport: %d", clientip, clientport);

        // 将建立好连接的新fd插入到数组中
        int pos = 1;
        for (; pos < fd_num_max; pos++){
            if (fd_array[pos] != defaultfd){
                continue;
            }
            else{
                break;
            }
        }
        // 如果数组已满，表示select可以监听的文件描述符达到上限，关闭对应的连接
        if (pos == fd_num_max){
            log(Warning, "Server is full, close fd: %d", newfd);
            close(newfd);
            return;
        }
        else{
            fd_array[pos] = newfd;
        }
    }

    // 读事件处理器
    void Reader(const int &fd, const int &pos){
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
        // 读取为空，说明客户端已经关闭，正常关闭文件描述符，同时将数组中对应位置设置为初始值
        if (n == 0){
            log(INFO, "client quit, me too, close fd is: %d", fd);
            close(fd);
            fd_array[pos] = defaultfd;
        }
        else if (n < 0){
            log(Error, "read error, fd is: %d", fd);
            close(fd);
            fd_array[pos] = defaultfd;
        }
        else{
            buffer[n] = 0;
            log(INFO, "get a message: %s, fd is: %d", buffer, fd);
        }
    }

    // 事件派发器
    void EventDispatcher(fd_set &rfds){
        for (int i = 0; i < fd_num_max; i++){
            if (fd_array[i] == defaultfd){
                continue;
            }
            else{
                if(FD_ISSET(fd_array[i], &rfds)){
                    if (fd_array[i] == _listensock.Fd()){
                        // 如果是连接事件 就调用连接事件处理器
                        log(INFO, "Get a new link...");
                        Accepter();
                    }
                    else{
                        // 如果是读事件 就调用读事件处理器
                        Reader(fd_array[i], i);
                    }
                }
            }
        }
    }

public:

    void Start(){
        int listenfd = _listensock.Fd();
        fd_array[0] = listenfd;
        while(true){
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = fd_array[0];

            // 遍历数组 并将所有fd设置到集合中 同时找到最大fd
            for (int i = 0; i < fd_num_max; i++){
                if (fd_array[i] == defaultfd){
                    continue;
                }
                else{
                    FD_SET(fd_array[i], &rfds);        
                    if (maxfd < fd_array[i]){
                        log(INFO, "maxfd updata..., maxfd is: %d", maxfd);
                        maxfd = fd_array[i];
                    }
                }
            }

            // 设置间隔时间
            struct timeval timeout = {2, 0};

            // 调用select函数开始监听集合中的所有fd
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout);
            switch(n){
                case 0:
                    // 等待超时
                    log(INFO, "select time out..., Seconds: %d, MicroSeconds: %d", timeout.tv_sec, timeout.tv_usec);
                    break;
                case -1:
                    // 出错
                    log(Error, "select error...");
                    break;
                default:
                    // 有事件触发
                    log(INFO, "There is an event triggered...");
                    EventDispatcher(rfds);
                    break;
            }
        }
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    MySocket _listensock;
    uint16_t _port;
    int fd_array[fd_num_max];
};